/*
    Contact info:
    bhepple@freeshell.org
    http://bhepple.freeshell.org/scrmgr

    Copyright (C) 1991-2008 Bob Hepple

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

*/
/* Simple program to run a program every X seconds and show the results
   via a cureses updated screen */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/wait.h>
#if USE_NCURSES
#include <ncurses/curses.h>
#else
#include <curses.h>
#endif

#if UNSIGNED_CHAR
#define CHAR			unsigned char
#else
#define CHAR			char
#endif

#ifdef __STDC__	/* encompass ANSI and non-ANSI function prototypes */

#include <stdlib.h>

#define NOARGS	(void)
#define ARG1(t1, v1) (t1 v1)
#define ARG2(t1, v1, t2, v2) (t1 v1, t2 v2)
#define ARG3(t1, v1, t2, v2, t3, v3) (t1 v1, t2 v2, t3 v3)
#define ARG4(t1, v1, t2, v2, t3, v3, t4, v4) (t1 v1, t2 v2, t3 v3, t4 v4)
#define ARG5(t1, v1, t2, v2, t3, v3, t4, v4, t5, v5) (t1 v1, t2 v2, t3 v3, t4 v4, t5 v5)
#define ARG6(t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6) (t1 v1, t2 v2, t3 v3, t4 v4, t5 v5, t6 v6)
#define HARG7(t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6, t7, v7) (t1 v1, t2 v2, t3 v3, t4 v4, t5 v5, t6 v6, t7 v7)

#define HARG1(t1, v1) (t1 v1)
#define HARG2(t1, v1, t2, v2) (t1 v1, t2 v2)
#define HARG3(t1, v1, t2, v2, t3, v3) (t1 v1, t2 v2, t3 v3)
#define HARG4(t1, v1, t2, v2, t3, v3, t4, v4) (t1 v1, t2 v2, t3 v3, t4 v4)
#define HARG5(t1, v1, t2, v2, t3, v3, t4, v4, t5, v5) (t1 v1, t2 v2, t3 v3, t4 v4, t5 v5)
#define HARG6(t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6) (t1 v1, t2 v2, t3 v3, t4 v4, t5 v5, t6 v6)
#define HARG7(t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6, t7, v7) (t1 v1, t2 v2, t3 v3, t4 v4, t5 v5, t6 v6, t7 v7)

#else

#define void int
#define NOARGS ()
#define ARG1(t1, v1) (v1) t1 v1;
#define ARG2(t1, v1, t2, v2) (v1, v2) t1 v1; t2 v2;
#define ARG3(t1, v1, t2, v2, t3, v3) (v1, v2, v3) t1 v1; t2 v2; t3 v3;
#define ARG4(t1, v1, t2, v2, t3, v3, t4, v4) (v1, v2, v3, v4) t1 v1; t2 v2; t3 v3; t4 v4;
#define ARG5(t1, v1, t2, v2, t3, v3, t4, v4, t5, v5) (v1, v2, v3, v4, v5) t1 v1; t2 v2; t3 v3; t4 v4; t5 v5;
#define ARG6(t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6) (v1, v2, v3, v4, v5, v6) t1 v1; t2 v2; t3 v3; t4 v4; t5 v5; t6 v6;
#define ARG7(t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6, t7, v7) (v1, v2, v3, v4, v5, v6, v7) t1 v1; t2 v2; t3 v3; t4 v4; t5 v5; t6 v6; t7 v7;

#define HARG1(t1, v1) ()
#define HARG2(t1, v1, t2, v2) ()
#define HARG3(t1, v1, t2, v2, t3, v3) ()
#define HARG4(t1, v1, t2, v2, t3, v3, t4, v4) ()
#define HARG5(t1, v1, t2, v2, t3, v3, t4, v4, t5, v5) ()
#define HARG6(t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6) ()
#define HARG7(t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6, t7, v7) ()

extern int	abs	HARG1(int,	i);
extern int	atoi	HARG1(char *,	c);
extern double	atof	HARG1(char *,	c);
extern long	atol	HARG1(char *,	c);
extern int	errno;
extern CHAR	*getenv	HARG1(char *,	c);
extern CHAR	*malloc	HARG1(int,	i);
extern int	free	HARG1(char *,	c);
extern CHAR	*realloc HARG1(char *,	c);
#endif

CHAR *smdisp_rcsid(void) {
    return("$Id: smdisp.c,v 1.8 2008/02/20 13:20:32 bhepple Exp $");
}

static char *usage_msg[] = {
    "Refresh the screen every t seconds (default 1) with the results of 'command'\n\n",
    "If last_line is given, that's where the display ends. -h gives the head.\n",
    "-i ignores 'q' key\n",
    "Use cursor keys (terminfo) and: (case insensitive)\n",
    "	1, a, ^a:	Start of display\n",
    "	$, e, ^E, g:	End of display\n",
    "	k, ^k, p, ^p:	Back one line\n",
    "	j, ^j, n, ^n:	Forward one line\n",
    "	u, ^u:		Back one page\n",
    "	d, ^d, v, ^v:	Forward one page (also, SPACE)\n",
    "	q:		quit unless the -i option is given\n",
    "	^c:		quits\n",
    "\n",
    "If 'command' has options, precede it with ' -- '\n",
    NULL
};

void usage ARG1(char *, progname)
{
    char **s;

    endwin();
    fprintf(stderr, "\nUsage: %s [-t interval] [-i] [-l last_line] [-h] command\n", progname);
    s = usage_msg;
    while (*s)
	fputs(*s++, stderr);
    exit(1);
}

int main ARG2(int, argc, char **, argv)
{
    int		time = 1, fd[2], pid, status;
    int		row, col, finished = 0, ignore_keys = 0;
    int		num_lines, this_line, last_line = -1, c;
    char	buf[100];
    FILE	*f;
    extern char	*optarg;
    extern int	optind;

    initscr();
    while ((c = getopt(argc, argv, "iht:l:")) != EOF)
        switch (c) {
                case 't':
                    time = atoi(optarg);
                    if (time <= 0)
                        usage(argv[0]);
                    break;
                case 'i':
                    ignore_keys = 1;
                    break;
                case 'h':
                    last_line = LINES;
                    break;
                case 'l':
                    last_line = atoi(optarg);
                    if (last_line < LINES)
                        last_line = LINES;
                    break;
                default:  
                    usage(argv[0]); 
                    break;
        }
    
    if ((optind >= argc) || (strlen(argv[optind]) == 0))
        usage(argv[0]);
    else
        argv += optind;

    nodelay(stdscr, 1);
    noecho();
    nonl();
    keypad(stdscr, 1);
    cbreak();

    while (!finished) {
        move(0, 0);
        clrtobot();
        /* set up command */
        if (pipe(fd) < 0) {
            perror("pipe failed");
            _exit(-1);
        }
        if ((pid = vfork()) == -1) {
            perror("fork failed");
            _exit(-1);
        }
        if (pid == 0) { /* the child */
            close(fd[0]);
            close(1);
            dup(fd[1]);
            close(fd[1]);
            execvp(argv[0], argv);
            perror(*argv);
            perror("execvp");
            _exit(-1);
        }
        /* The parent */
        close(fd[1]);
        this_line = 0;
        if ((f = fdopen(fd[0], "r")) != NULL) {
            while (fgets(buf, 100, f) != NULL) {
                this_line++;
                if ((last_line < 0) || (this_line < last_line)) {
                    getyx(stdscr, row, col);
                    if (row == LINES - 1) {
                        move(0, 0);
                        deleteln();
                        move(LINES - 2, col);
                    }
                    addstr(buf);
                }
            }
        }
        num_lines = this_line;

        close(fd[0]);
        fclose(f);
        refresh();
        c = wgetch(stdscr);
        if (c != ERR) {
            c = tolower(c);
            if (last_line < 0)
                last_line = num_lines;
            switch (c) {
                    case '+':
                    case 'k':
                    case 'p':
                    case '\020': /* ^p */
                    case '\013': /* ^k */
                    case KEY_UP:
                        if (last_line > 1)
                            last_line--;
                        break;
                    case '-':
                    case 'j':
                    case 'n':
                    case '\016': /* ^n */
                    case '\012': /* ^j */
                    case KEY_DOWN:
                        last_line++;
                        break;
                    case 'u':
                    case '\025': /* ^u */
                    case KEY_PPAGE:
                        last_line -= LINES;
                        break;
                    case 'd':
                    case 'v':
                    case ' ':
                    case '\026':
                    case '\004': /* ^d */
                    case KEY_NPAGE:
                        last_line += LINES;
                        break;
                    case '1':
                    case 'a':
                    case '\001': /* ^a */
                    case KEY_HOME:
                        last_line = 1;
                        break;
                    case 'e':
                    case '\005': /* ^e */
                    case '$':
                    case 'g':
                    case KEY_LL:
                    case KEY_END:
                        last_line = num_lines;
                        break;
                    case 'q':
                        if (!ignore_keys)
                            finished = 1;
                        break;
                    case '\003': /* ^c */
                        finished = 1;
                        break;
                    default:
                        break;
            }
            if (last_line < LINES)
                last_line = LINES;
            if (last_line >= num_lines)
                last_line = -1;
        } else
            sleep(time);
        wait(&status);
    }
    endwin();
    return(0);
}

/* For emacs:
 * Local Variables:
 * tab-width: 8
 * End:
 */
